package com.xiaoq.users.service;

import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.text.MessageFormat;
import java.util.Date;
import java.util.Map;
import java.util.Properties;

import javax.imageio.ImageIO;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.Base64Utils;
import org.springframework.web.client.RestTemplate;

import com.google.code.kaptcha.Producer;
import com.google.code.kaptcha.util.Config;
import com.xiaoq.SMSChannelConfItem;
import com.xiaoq.SMSChannelProperties;
import com.xiaoq.coms.util.DigestUtil;
import com.xiaoq.coms.util.IdGenerator;
import com.xiaoq.coms.util.JsonUtil;
import com.xiaoq.sms.SendSMSUtil;
import com.xiaoq.user.endpoint.VCodeVo;
import com.xiaoq.users.redis.RedisTemplate4JedisCluster;

/**
 * @author xinpeng created on 16/7/29-下午3:32.
 */
@Service
public class VerificationCodeService {
	
	private static final String LOG_PREFIX = "[USER] [VERI_CODE] [SMS] "; 
	
	private static final Logger LOG = LoggerFactory.getLogger(VerificationCodeService.class);
	
	private static final String CLOOPEN_URL_TPL = "https://app.cloopen.com:8883/2013-12-26/Accounts/{0}/SMS/TemplateSMS?sig={1}";
	
	private static final String CLOOPEN_SIG_TPL = "{0}{1}{2}";
	
	private static final String CLOOPEN_AUTH_TPL = "{0}:{1}";
	
	private static final String CLOOPEN_TS_FORMAT = "yyyyMMddHHmmss";
	
	private static final String CLOOPEN_RESP_OK = "000000";

	@Autowired
	private IdGenerator idGenerator;
	
    @Autowired
    private RedisTemplate4JedisCluster redisTemplate4JedisCluster;
    
    @Autowired
	private Properties captchaConfigProps;
    
    @Autowired
    private SMSChannelProperties smsChannelProps;
    
    @Autowired
    private SendSMSUtil sendSMSUtil;
	
	@Bean("captchaConfigProps")
	public Properties getCaptchaConfigProps() {
		ImageIO.setUseCache(false);
		Properties props = new Properties();
		props.put("kaptcha.border", "no");
		props.put("kaptcha.textproducer.font.color", "black");
		props.put("kaptcha.textproducer.char.space", "5");
		props.put("kaptcha.image.width", 80);
		props.put("kaptcha.image.height", 22);
		return props;
	}

    public VCodeVo createVCode() {
    	String key = idGenerator.nextId();
		Producer kaptchaProducer = null;
		Config config = new Config(captchaConfigProps);
		kaptchaProducer = config.getProducerImpl();
		String capText = kaptchaProducer.createText();
		LOG.info("图形验证码的内容:"+capText);
		BufferedImage bi = kaptchaProducer.createImage(capText);
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		try {
			ImageIO.write(bi, "jpg", baos);
			bi.flush();
			baos.flush();
			String vCode = Base64Utils.encodeToString(baos.toByteArray());
			redisTemplate4JedisCluster.setWithExpireTime("user.vcode", key, capText, 120);
			return new VCodeVo(key, vCode);
		} catch (IOException e) {
			throw new RuntimeException("输出验证码时遇到未知错误");
		} finally {
			IOUtils.closeQuietly(baos);
		}
    }
    
    public boolean verifyVCode(String key, String vCode){
        //暂时不做验证
        String  vCodeInRedis = redisTemplate4JedisCluster.get("user.vcode", key);
        return vCode.equals(vCodeInRedis);
    }
    
    public void createAndSendVerificationCode(String cellphoneNumber){

        //1、根据算法随机生成6位数字验证码
        String verificationCode = generateVerificationCode(true,6);

        //2、将验证码存入redis集群的缓存中,并设定过期时间为120秒
        redisTemplate4JedisCluster.setWithExpireTime("user.verificaitoncode",cellphoneNumber,verificationCode,120);

        //3、调用短信通道服务商的sdk,发送验证码给客户
        // 这里企图做一个时间上是否发送验证码校验。
//        String verificaition = redisTemplate4JedisCluster.get("user.verificaitoncode", verificationCode);
        //上线初始化用户时,暂时注释掉手机验证码的发送,modify by xinpeng 20170522
        String[] strArray={verificationCode,"2"};
        sendSMSUtil.sendSMS(cellphoneNumber, "228258", strArray);
//        sendSMSVerificationCode(cellphoneNumber,"140619",verificationCode,"2");
    }

    public boolean verifyVerificationCode(String cellphoneNumber,String verificationCode){
        //暂时不做验证,系统上线前初始化注册用户,modify by xinpeng 20170522
        String  verificationCodeInRedis = redisTemplate4JedisCluster.get("user.verificaitoncode", cellphoneNumber);
        return verificationCode.equals(verificationCodeInRedis);
    }

    private void senSMSVerificationCode(String phoneNo, String[] content) {
    	deliverValidationCode(smsChannelProps.getValidationCode(), phoneNo, content);
    }
    
    private String generateVerificationCode(boolean numberFlag,int lengthOfCode){

        String retStr = "";
        String strTable = numberFlag ? "1234567890" : "1234567890abcdefghijkmnpqrstuvwxyz";
        int len = strTable.length();
        boolean bDone = true;
        do {
            retStr = "";
            int count = 0;
            for (int i = 0; i < lengthOfCode; i++) {
                double dblR = Math.random() * len;
                int intR = (int) Math.floor(dblR);
                char c = strTable.charAt(intR);
                if (('0' <= c) && (c <= '9')) {
                    count++;
                }
                retStr += strTable.charAt(intR);
            }
            if (count >= 2) {
                bDone = false;
            }
        } while (bDone);
        return retStr;

    }
    
	private void deliverValidationCode(SMSChannelConfItem conf, String phoneNo, String[] content) {
		HttpHeaders headers = new HttpHeaders();
		headers.add("Content-Type", MediaType.APPLICATION_JSON_UTF8_VALUE);
		headers.add("Accept", MediaType.APPLICATION_JSON_VALUE);
		
		String timestamp = DateFormatUtils.format(new Date(), CLOOPEN_TS_FORMAT);
		byte[] authBytes = null;
		try {
			authBytes = MessageFormat.format(CLOOPEN_AUTH_TPL, conf.getAccountId(), timestamp).getBytes("UTF-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		String auth = Base64Utils.encodeToString(authBytes);
		headers.add("Authorization", auth);
		
		String sigContent = MessageFormat.format(CLOOPEN_SIG_TPL, conf.getAccountId(), conf.getAuthToken(), timestamp);
		String sig = DigestUtil.md5(sigContent).toUpperCase();
		URI uri = null;
		try {
			uri = new URI(MessageFormat.format(CLOOPEN_URL_TPL, conf.getAccountId(), sig));
		} catch (URISyntaxException e) {
			e.printStackTrace();
		}
		if (LOG.isDebugEnabled()) {
			LOG.debug(uri.toString());
		}
		CloopenSMSBody body = new CloopenSMSBody(conf.getAppId(), conf.getTemplateId(),phoneNo, content);
		RequestEntity request = new RequestEntity(body, headers, HttpMethod.POST, uri);
		
		LOG.info(LOG_PREFIX + "通过CLOOPEN短信网关发送短信...");
		ResponseEntity<Map<String, Object>> resp = null;
		RestTemplate rest = new RestTemplate();
		try {
			resp = rest.exchange(request, new ParameterizedTypeReference<Map<String, Object>>(){});
		} catch (Exception e) {
			String msg = LOG_PREFIX + "调用CLOOPEN短信网关出现未知错误，" + e.getMessage();
			LOG.error(msg);
			throw new RuntimeException(msg, e);
		}
		if (resp == null) {
			String msg = LOG_PREFIX + "调用CLOOPEN短信网关出现未知错误";
			LOG.error(msg);
			throw new RuntimeException(msg);
		}
		HttpStatus respStatus = resp.getStatusCode();
		if (HttpStatus.OK.equals(respStatus)) {
			Map<String, Object> respBody = resp.getBody();
			String bizStatusCode = (String)respBody.get("statusCode");
			if (CLOOPEN_RESP_OK.equals(bizStatusCode)) {
				LOG.info(LOG_PREFIX + "通过CLOOPEN短信网关发送短信完毕");
			} else {
				String msg = MessageFormat.format(LOG_PREFIX + "通过CLOOPEN短信网关发送短信返回错误码={0}，{1}", bizStatusCode, respBody.get("statusMsg"));
				LOG.error(msg);
				throw new RuntimeException(msg);
			}
		} else {
			String respAsString = JsonUtil.asString(resp.getBody());
			String msg = LOG_PREFIX + "调用CLOOPEN短信网关返回异常" + respAsString;
			LOG.error(msg);
			throw new RuntimeException(msg);
		}
	}
	

	@SuppressWarnings("unused")
	private static final class CloopenSMSBody {
		private String appId;
		private String templateId;
		private String to;
		private Object[] datas;
		
		public CloopenSMSBody(String appId, String templateId, String to, Object[] datas) {
			this.appId = appId;
			this.templateId = templateId;
			this.to = to;
			this.datas = datas;
		}
		
		public String getAppId() {
			return appId;
		}
		public void setAppId(String appId) {
			this.appId = appId;
		}
		public String getTemplateId() {
			return templateId;
		}
		public void setTemplateId(String templateId) {
			this.templateId = templateId;
		}
		public String getTo() {
			return to;
		}
		public void setTo(String to) {
			this.to = to;
		}
		public Object[] getDatas() {
			return datas;
		}
		public void setDatas(Object[] datas) {
			this.datas = datas;
		}
	}
}
